﻿namespace MsForgeReboot.Services.Tests {
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Moq;

    using MsForgeReboot.Framework.Entities;
    using MsForgeReboot.Services.Tests.Fakes;

    #endregion

    [TestClass]
    public class UserGroupServiceTests {
        private Mock<MsForgeContext> contextMock;

        [TestInitialize]
        public void TestInitialize() {
            this.contextMock = new Mock<MsForgeContext>();

            this.contextMock.SetupGet(data => data.UserGroups).Returns(TestDataGenerator.GetFakeUserGroupInstances(15));
            this.contextMock.SetupGet(data => data.Users).Returns(TestDataGenerator.GetFakeUserInstances(15));

            foreach (var userGroup in this.contextMock.Object.UserGroups) {
                userGroup.Author = this.contextMock.Object.Users.Single(u => u.Id == userGroup.Id);
            }
        }

        [TestCleanup]
        public void TestCleanup() {
            this.contextMock = null;
        }

        [TestMethod]
        public void InstantiateUserGroupService() {
            var userGroupService = new UserGroupService(this.contextMock.Object);
            Assert.IsNotNull(userGroupService);
        }

        [TestMethod]
        public void GetAllReturnsPagedResults() {
            var userGroupService = new UserGroupService(this.contextMock.Object);

            var actual = userGroupService.GetList(1, 10);

            Assert.AreEqual(10, actual.Count);
            Assert.AreEqual(1, actual.PageNumber);
            Assert.AreEqual(2, actual.PageCount);
        }

        [TestMethod]
        public void GetAllEagerLoadAuthors()
        {
            var userGroupService = new UserGroupService(this.contextMock.Object);

            var actual = userGroupService.GetList(1, 10);

            foreach (var userGroup in actual) {
                Assert.AreNotEqual(0, userGroup.Author.Id);
            }
        }

        [TestMethod]
        public void DeleteByUrlPointerRemovesItemFromTheDataContext() {
            var userGroupService = new UserGroupService(this.contextMock.Object);

            Assert.IsNotNull(
                this.contextMock.Object.UserGroups.FirstOrDefault(item => item.UrlPointer == "user-group-3"));
            userGroupService.Delete("user-group-3");

            Assert.IsNull(this.contextMock.Object.UserGroups.FirstOrDefault(item => item.UrlPointer == "user-group-3"));
            this.contextMock.Verify(ctx => ctx.SaveChanges(), Times.Once);
        }

        [TestMethod]
        public void GetByUrlPointerReturnsUserGroup() {
            var userGroupService = new UserGroupService(this.contextMock.Object);

            var actual = userGroupService.GetByUrlPointer("user-group-6");

            Assert.IsInstanceOfType(actual, typeof(UserGroup));
        }

        [TestMethod]
        public void GetByUrlPointerReturnsUserGroupEagerLoadsAuthor()
        {
            var userGroupService = new UserGroupService(this.contextMock.Object);

            var actual = userGroupService.GetByUrlPointer("user-group-6");

            Assert.IsNotNull(actual.Author);
        }

        [TestMethod]
        public void GetByUrlPointerReturnsCorrectUserGroup() {
            var userGroupService = new UserGroupService(this.contextMock.Object);
            const string Expected = "user-group-6";

            var actual = userGroupService.GetByUrlPointer(Expected);

            Assert.AreEqual(actual.UrlPointer, Expected);
        }

        [TestMethod]
        public void SaveShouldInsertNewUserGroupIfIdIsZero() {
            this.contextMock.Setup(context => context.SaveChanges());

            var userGroupService = new UserGroupService(this.contextMock.Object);
            var newItem = new UserGroup { Id = 0, Title = "New Item", Author = new User() { Id = 7 }};

            userGroupService.Save(newItem);

            Assert.AreEqual(16, this.contextMock.Object.UserGroups.Count());
        }

        [TestMethod]
        public void AuthorIsCorrectlyAssignedToUserGroupOnSave() {
          
            this.contextMock.Setup(context => context.SaveChanges());

            var userGroupService = new UserGroupService(this.contextMock.Object);
            var newItem = new UserGroup { Id = 0, Title = "New Item", Author = new User() { Id = 7 }};

            userGroupService.Save(newItem);

            Assert.AreEqual(newItem.Author.Id, 7);
            Assert.AreEqual(newItem.Author.PublicName, "User 7");
            Assert.AreEqual(newItem.Author.Username, "user-name-7@domain7.com");

        }

        [TestMethod]
        public void SaveShouldUpdateExistingUserGroupIfIdIsNotEqualToZero() {
            this.contextMock.Setup(context => context.SaveChanges());

            var userGroupService = new UserGroupService(this.contextMock.Object);
            var expected = new UserGroup { Id = 5, Title = "Updated Title", Author = new User() { Id = 7 } };

            userGroupService.Save(expected);

            var actual = this.contextMock.Object.UserGroups.Single(item => item.Id == 5);

            Assert.AreEqual(15, this.contextMock.Object.UserGroups.Count());
            Assert.AreEqual(expected.Title, actual.Title);
        }

        [TestMethod]
        public void GetAllReturnsResultsSortedByTitle() {
            var fakseUserGroupInstances =
                new InMemoryDbSet<UserGroup>(TestDataGenerator.GetFakeUserGroupInstances(15).OrderBy(item => Guid.NewGuid()));
            this.contextMock.SetupGet(data => data.UserGroups).Returns(fakseUserGroupInstances);

            var userGroupService = new UserGroupService(this.contextMock.Object);

            var actual = userGroupService.GetList(1, 10);

            Assert.AreEqual("User Group 1", actual[0].Title);
            Assert.AreEqual("User Group 10", actual[1].Title);
            Assert.AreEqual("User Group 11", actual[2].Title);
            Assert.AreEqual("User Group 12", actual[3].Title);
            Assert.AreEqual("User Group 13", actual[4].Title);
            Assert.AreEqual("User Group 14", actual[5].Title);
            Assert.AreEqual("User Group 15", actual[6].Title);
            Assert.AreEqual("User Group 2", actual[7].Title);
            Assert.AreEqual("User Group 3", actual[8].Title);
        }

    }
}